using System;
using System.Collections.Generic;
using System.Text;
using Hoshimi._Map_.IDManager;
using System.Drawing;


namespace Hoshimi._Map_._Objective_
{
    public abstract class ObjectivePoint
    {
        // VARIABLES
        protected byte mID = 0;
        protected Point mLocation = Point.Empty;

        // GETTERS & SETTERS
        public byte myID
        { 
            get { return mID; } 
        }
        public Point myLocation
        {
            set { mLocation = value; }
            get { return mLocation; } 
        }
        
		public ObjectiveIDManager		myObjectiveIDManager	{get { return ObjectiveIDManager.getInstance();}}
		
		public static MyMain			myPlayer			{ get { return myMapManager.myPlayer; } }
        public static BotManager		myBotManager		{ get { return BotManager.Instance(); } }
        public static MapManager		myMapManager		{ get { return MapManager.getInstance(); } }
        public static PathManager		myPathManager		{ get { return PathManager.getInstance(); } }
		public static StrategyManager	myStrategyManager	{ get { return StrategyManager.Instance(); } }
		public static AIPathManager		myAIPathManager		{ get { return AIPathManager.Instance(); } }

		public static ObjectiveType GetNearestObjective<ObjectiveType>(Point point, Dictionary<byte, ObjectiveType> objectiveMap, ref PathFinderConfig pathFinderConfig) where ObjectiveType : ObjectivePoint
        {
			int cost;
			return GetNearestObjective(point, objectiveMap, ref pathFinderConfig, out cost);
        }

		// METHODES
        public static ObjectiveType GetNearestObjective<ObjectiveType>(Point point, Dictionary<byte, ObjectiveType> objectiveMap, ref PathFinderConfig pathFinderConfig, out int cost) where ObjectiveType : ObjectivePoint
        {
            ObjectiveType nearestObjective	= null;
            int distanceMin					= int.MaxValue;
            int distance					= 0;
			bool throughEnnemyZone			= true;
			PathFinderResult pathResult;

            foreach (KeyValuePair<byte, ObjectiveType> kvp in objectiveMap)
            {
				ObjectiveType objective = kvp.Value;

				if(!objective.IsValidObjective())
				{
					continue;
				}

				// calcul de la distance minimum
				distance = Utilitaire.ManathanDistance(point, objective.myLocation);
				if (distance > distanceMin)
				{
					continue;
				}

				// calcul de la vrai distance
                distance = myPathManager.GetTurnCost(point, objective.myLocation, ref pathFinderConfig, out pathResult);
                
				if (pathFinderConfig.mUseEnnemy && 
					!throughEnnemyZone			&& 
					pathResult.mThroughEnnemyZone)
				{
					continue;
				}
				
				if(distance < distanceMin)
                {
                    distanceMin			= distance;
                    nearestObjective	= objective;
					throughEnnemyZone	= pathResult.mThroughEnnemyZone;
                }
            }

			cost = distanceMin;

            return nearestObjective;
        }

		public static ObjectiveType GetObjectiveInWay<ObjectiveType>(Point startPoint, Point endPoint, Dictionary<byte, ObjectiveType> objectiveMap, ref PathFinderConfig pathFinderConfig) where ObjectiveType : ObjectivePoint
        {
			int cost;
			return GetObjectiveInWay(startPoint, endPoint, objectiveMap, ref pathFinderConfig, out cost);
        }

		public static ObjectiveType GetObjectiveInWay<ObjectiveType>(Point startPoint, Point endPoint, Dictionary<byte, ObjectiveType> objectiveMap, ref PathFinderConfig pathFinderConfig, out int cost) where ObjectiveType : ObjectivePoint
        {
            ObjectiveType nearestObjective		= null;
            int distanceMin						= int.MaxValue;
            int distance						= 0;
			bool throughEnnemyZone				= true;
			PathFinderResult pathResult;

            foreach (KeyValuePair<byte, ObjectiveType> kvp in objectiveMap)
            {
				ObjectiveType objective = kvp.Value;

				if(!objective.IsValidObjective())
				{
					continue;
				}

				// Calcul de la distance minimum
				distance = Utilitaire.ManathanDistance(startPoint, objective.myLocation);
				distance += Utilitaire.ManathanDistance(objective.myLocation, endPoint);

				if (distance >= distanceMin)
				{
					continue;
				}

				// Calcul de la vrai distance
                distance = myPathManager.GetTurnCost(startPoint, objective.myLocation, ref pathFinderConfig, out pathResult);
				
				if(distance >= int.MaxValue)
				{
					continue;
				}

				if (pathFinderConfig.mUseEnnemy && 
					!throughEnnemyZone			&& 
					pathResult.mThroughEnnemyZone)
				{
					continue;
				}
				
				distance += myPathManager.GetTurnCost(objective.myLocation, endPoint, ref pathFinderConfig, out pathResult);
                
				if (pathFinderConfig.mUseEnnemy && 
					!throughEnnemyZone			&& 
					pathResult.mThroughEnnemyZone)
				{
					continue;
				}
				
				if(distance < distanceMin)
                {
                    distanceMin			= distance;
                    nearestObjective	= objective;
					throughEnnemyZone	= pathResult.mThroughEnnemyZone;
                }
            }

			cost = distanceMin;

            return nearestObjective;
        }

		protected virtual bool IsValidObjective()
		{
			return true;
		}

        // Ctor
        public ObjectivePoint(Point point)
        {
            myLocation  = point;
            mID         = (byte) myObjectiveIDManager.getID();
        }
    }
}
